package com.katesoft.scale4j.rttp.jobs;

import static com.katesoft.scale4j.common.services.IBeanNameReferences.DATASOURCE_TRANSACTION_MANAGER;

import java.util.Arrays;
import java.util.Collection;

import org.apache.commons.lang.ArrayUtils;
import org.perf4j.StopWatch;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SchedulerAccessor;
import org.springframework.transaction.PlatformTransactionManager;

import com.katesoft.scale4j.common.services.IBeanNameReferences;
import com.katesoft.scale4j.log.LogFactory;
import com.katesoft.scale4j.log.Logger;

/**
 * This class allows to register and schedule jobs(triggers) as well as listeners within Quartz job
 * scheduler.
 * <p/>
 * You can set autoDetectJobs flag to true and do not enumerate jobs/triggers or pass collection of
 * triggers/jobDetails that need to be registered(if autoDetect is not acceptable behaviour).
 * 
 * @author kate2007
 */
public class SchedulerJobsRegistrator extends SchedulerAccessor implements ApplicationContextAware,
         InitializingBean {
   private final Logger logger = LogFactory.getLogger(getClass());
   private ApplicationContext applicationContext;
   private boolean autoDetectJobs = true;

   /**
    * this class is able to autoDetect trigger that are defined in ApplicationContext and this
    * feature is enabled by default.
    * <p/>
    * This may not be acceptable behaviour for some application that would like to enumerate all
    * trigger manually and skip some at all. In this case set autoDetectJobs to false and inject
    * triggers and jobDetails(if necessary) manually.
    * 
    * @param autoDetectJobs
    *           derive triggers and job details from ApplicationContext automatically.
    */
   public void setAutoDetectJobs(boolean autoDetectJobs) {
      this.autoDetectJobs = autoDetectJobs;
   }

   public boolean isAutoDetectJobs() {
      return autoDetectJobs;
   }

   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      this.applicationContext = applicationContext;
   }

   @Override
   public void setJobDetails(JobDetail[] jobDetails) {
      super.setJobDetails(jobDetails);
      if (jobDetails != null && !ArrayUtils.isEmpty(jobDetails)) {
         logger.debug("setting auto_detect=[false], jobDetails %s provided",
                  Arrays.toString(jobDetails));
         setAutoDetectJobs(false);
      }
   }

   @Override
   public void setTriggers(Trigger[] triggers) {
      if (triggers != null && !ArrayUtils.isEmpty(triggers)) {
         super.setTriggers(triggers);
         logger.debug("setting auto_detect=[false], triggers %s provided",
                  Arrays.toString(triggers));
         setAutoDetectJobs(false);
      }
   }

   @Override
   public void afterPropertiesSet() throws SchedulerException {
      setTransactionManager(applicationContext.getBean(DATASOURCE_TRANSACTION_MANAGER,
               PlatformTransactionManager.class));
      StopWatch stopWatch = new StopWatch("rttp.scheduler.jobs.registration");
      try {
         if (isAutoDetectJobs()) {
            Collection<Trigger> triggers = applicationContext.getBeansOfType(Trigger.class)
                     .values();
            super.setTriggers(triggers.toArray(new Trigger[triggers.size()]));
            logger.debug(" %s triggers were auto registered", triggers.size());
            Collection<JobDetail> jobDetails = applicationContext.getBeansOfType(JobDetail.class)
                     .values();
            super.setJobDetails(jobDetails.toArray(new JobDetail[jobDetails.size()]));
            logger.debug(" %s jobDetails were auto registered", jobDetails.size());
         }
         registerListeners();
         registerJobsAndTriggers();
      } catch (RuntimeException e) {
         logger.error(e);
         throw e;
      } finally {
         stopWatch.stop();
         logger.debug("jobs/triggers/listeners were registered in %s milisecs",
                  stopWatch.getElapsedTime());
      }
   }

   @Override
   protected Scheduler getScheduler() {
      return (Scheduler) applicationContext.getBean(IBeanNameReferences.RTTP_CLIENT_SCHEDULER);
   }
}
